/*
 * Copyright (C) 2010 ST-Ericsson AS
 * Author: Erwan Bracq / erwan.bracq@stericsson.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 */

#define MID_GPIO_USED_INLINE

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "mid_log.h"
#include "mid_gpio.h"
#include "mid_error.h"
#include "mid_gpio_internal.h"

static const char* gpio_display_name(enum gpio_id id)
{
	switch (id) {
	case GPIO_ID_ON:
		return("GPIO_MOD_ON");
		break;
	case GPIO_ID_RESET:
		return("GPIO_MOD_RESET");
		break;
	case GPIO_ID_RESOUT2:
		return("GPIO_MOD_RESOUT2");
		break;
	case GPIO_ID_SERVICE:
		return("GPIO_MOD_SERVICE");
		break;
	case GPIO_ID_PWRRSTIN:
		return("GPIO_MOD_PWRRSTIN");
		break;
	case GPIO_ID_PWRRSTOUT:
		return("GPIO_MOD_PWRRSTOUT");
		break;
	case GPIO_ID_COUNT: /* Fall though to error handling */
	default:
		MLGE("GPIO: Invalid gpio ID: %d", id);
		return("INVALID");
	}
}

static inline bool mid_gpio_used(struct mid_gpio_context* context, enum gpio_id gpio)
{
        return (context->mid_gpios_tab[gpio].gpio != GPIO_PHYSICAL_INVALID);
}

static inline bool mid_get_gpio_aclo(struct gpio *gpio)
{
	return gpio->active_low;
}

static inline bool mid_get_gpio_ctrl_enable(struct gpio *gpio)
{
	return gpio->gpio_ctrl_enable;
}

static inline bool mid_get_gpio_ctrl_noaclow(struct gpio *gpio)
{
	return gpio->gpio_ctrl_noaclow;
}

static inline bool mid_get_gpio_direction(struct gpio *gpio)
{
	return gpio->direction;
}

static inline void mid_set_gpio_val(struct gpio *gpio, int val)
{
	gpio->value = val;
}

static int mid_gpio_set_value(struct mid_gpio_context* context, enum gpio_id gpio_id, int value, int loc_ctrl_enable)
{
	int res = 0;
	int len = 0;
	char gpio_value[GPIO_VALUE_SZ];
	if (value < 0) {
		MLGE("GPIO: Invalid GPIO value (negative): %d", value);
		return -EMIDGPIO;
	}

	len = snprintf(gpio_value, GPIO_VALUE_SZ, "%d", value);
	if (len < 0) {
		res = errno;
		MLGE("GPIO: Failed to translate GPIO value to string: %s",
			strerror(res));
		return res;
	}
	if (loc_ctrl_enable && mid_gpio_used(context, gpio_id)) {
		apr_size_t num_written;
		apr_status_t status;

		status = apr_file_write_full(context->value_fds[gpio_id], gpio_value, len, &num_written);
		if(status != APR_SUCCESS) {
			char message[100];
			MLGE("GPIO: Failed to write GPIO value: %s",
				apr_strerror(status, message, sizeof(message)));
			return -1;
		}
		MLGD("GPIO: Write value %s to GPIO %s", gpio_value, gpio_display_name(gpio_id));
	}
	return 0;
}

static int mid_gpio_request(struct mid_gpio_context* context, enum gpio_id gpio)
{
	int res = 0;
	int fd = -1;
	char gpio_name[GPIO_NAME_SZ];
	char gpio_export[GPIO_SYSFS_SZ];
	bool loc_ctrl_enable;

	if (gpio > GPIO_ID_HIGHEST) {
		MLGE("GPIO: Invalid GPIO number (too large): %d", gpio);
		return -EMIDGPIO;
	}
	loc_ctrl_enable = mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[gpio]);

	res = snprintf(gpio_export, GPIO_SYSFS_SZ, GPIO_EXPORT, context->gpio_base_path);
	if (res < 0) {
		res = errno;
		MLGE("GPIO: Failed to translate GPIO export entry into a string: %s",
			strerror(res));
		return res;
	}
	res = snprintf(gpio_name, GPIO_NAME_SZ, "%d", context->mid_gpios_tab[gpio].gpio);
	if (res < 0) {
		res = errno;
		MLGE("GPIO: Failed to translate GPIO id to string: %s",
			strerror(res));
		return res;
	}

	if (loc_ctrl_enable && mid_gpio_used(context, gpio)) {
		fd = open(gpio_export, O_WRONLY);
		if (fd == -1) {
			res = errno;
			MLGE("GPIO: Failed to open %s: %s", gpio_export, strerror(res));
			return res;
		}
		MLGD("GPIO: Writing value %s to GPIO %s", gpio_name, gpio_export);
		if (write(fd, gpio_name, res) == -1) {
			res = errno;
			MLGE("GPIO: Failed to write GPIO export request: %s",
				strerror(res));
			return res;
		}
		if (close(fd)) {
			res = errno;
			MLGE("GPIO: Failed to close %s: %s", gpio_export, strerror(res));
			return res;
		}
		MLGD("GPIO: Successfuly exported GPIO %s", gpio_name);
	}
	return 0;
}

static int mid_gpio_free(struct mid_gpio_context* context, enum gpio_id gpio)
{
	int res = 0;
	char gpio_name[GPIO_NAME_SZ];
	if (gpio > GPIO_ID_HIGHEST) {
		MLGE("GPIO: Invalid GPIO number (too large): %d", gpio);
		return -EMIDGPIO;
	}

	res = snprintf(gpio_name, GPIO_NAME_SZ, "%d", context->mid_gpios_tab[gpio].gpio);
	if (res < 0) {
		res = errno;
		MLGE("GPIO: Failed to translate GPIO id to string: %s",
			strerror(res));
		return res;
	}

	if (mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[gpio]) && mid_gpio_used(context, gpio)) {
		apr_status_t status;
		apr_size_t num_written;

		status = apr_file_write_full(context->unexport_fd, gpio_name, strlen(gpio_name), &num_written);
		if (status != APR_SUCCESS) {
			char message[100];
			MLGE("GPIO: Failed to write GPIO unexport request: %s",
				apr_strerror(status, message, sizeof(message)));
			return -1;
		}
		MLGD("GPIO: Write value %s to GPIO unexport file", gpio_name);
	}
	return 0;
}

#ifndef BEFORE_KERNEL_2_6_33
static int mid_gpio_set_edge(struct mid_gpio_context* context, enum gpio_id gpio, int edge, int loc_ctrl_enable)
{
	int res = 0;
	int fd = -1;
	char gpio_name[GPIO_SYSFS_SZ];
	char *gpio_edge = NULL;
	res = snprintf(gpio_name, GPIO_SYSFS_SZ, GPIO_EDGE, context->gpio_base_path, context->mid_gpios_tab[gpio].gpio);
	if (res < 0) {
		res = errno;
		MLGE("GPIO: Failed to translate GPIO id to string: %s",
			strerror(res));
		return res;
	}
	switch (edge) {
	case NONE:
		gpio_edge = "none";
		break;
	case RISING:
		gpio_edge = "rising";
		break;
	case FALLING:
		gpio_edge = "falling";
		break;
	case BOTH:
		gpio_edge = "both";
		break;
	default:
		MLGE("GPIO: Invalid edge mode request for GPIO: %d", gpio);
		return -EMIDGPIO;
	}
	if (loc_ctrl_enable && mid_gpio_used(context, gpio)) {
		fd = open(gpio_name, O_WRONLY);
		if (fd == -1) {
			res = errno;
			MLGE("Failed to open %s: %s", gpio_name, strerror(res));
			return res;
		}
		MLGD("GPIO: Writing value %s to GPIO %s", gpio_edge, gpio_name);
		if (write(fd, gpio_edge, strlen(gpio_edge)) == -1) {
			res = errno;
			MLGE("Failed to write GPIO edge value: %s",
				strerror(res));
			return res;
		}
		if (close(fd)) {
			res = errno;
			MLGE("Failed to close %s: %s", gpio_name,
				strerror(res));
			return res;
		}
		MLGD("GPIO: Successfully changed the triggering of GPIO %s", gpio_name);
	}
	return 0;
}

static int mid_gpio_set_irq_wake(struct mid_gpio_context* context, enum gpio_id gpio_id,
				 int enabled, int loc_ctrl_enable)
{
	char *gpio_irq_wake = NULL;

	if (enabled)
		gpio_irq_wake = "1";
	else
		gpio_irq_wake = "0";

	if (loc_ctrl_enable && mid_gpio_used(context, gpio_id)) {
		apr_size_t num_written;
		if (context->irqwake_fds[gpio_id] == NULL) {
			MLGE("Unable to modify irqwake for gpio %s, since the irqwake file could not be opened at startup", gpio_display_name(gpio_id));
			return -1;
		}

		apr_status_t status = apr_file_write_full(context->irqwake_fds[gpio_id], gpio_irq_wake, strlen(gpio_irq_wake), &num_written);
		if (status != APR_SUCCESS) {
			char message[100];
			MLGE("GPIO: Failed to write GPIO irq wake value: %s",
				apr_strerror(status, message, sizeof(message)));
			return -1;
		}
		MLGD("GPIO: Write value %s to GPIO %s", gpio_irq_wake, gpio_display_name(gpio_id));
	}
	return 0;
}



static int mid_gpio_set_active_low(struct mid_gpio_context* context, enum gpio_id gpio, int active_low, int loc_ctrl_enable)
{
	int res = 0;
	int fd = -1;
	int len = 0;
	char gpio_name[GPIO_SYSFS_SZ];
	char gpio_active_low[GPIO_VALUE_SZ];
	if ((active_low < ACTIVE_HIGH) || (active_low > ACTIVE_LOW)) {
		MLGE("GPIO: Invalid GPIO value for active_low field: %d", active_low);
		return -EMIDGPIO;
	}
	res = snprintf(gpio_name, GPIO_SYSFS_SZ, GPIO_ACTIVE_LOW, context->gpio_base_path, context->mid_gpios_tab[gpio].gpio);
	if (res < 0) {
		res = errno;
		MLGE("GPIO: Failed to translate GPIO id to string: %s",
			strerror(res));
		return res;
	}
	len = snprintf(gpio_active_low, GPIO_VALUE_SZ, "%d",
		active_low);
	if (len < 0) {
		res = errno;
		MLGE("GPIO: Failed to translate GPIO active low value to string: %s",
			strerror(res));
		return res;
	}
	if (loc_ctrl_enable && mid_gpio_used(context, gpio)) {
		fd = open(gpio_name, O_WRONLY);
		if (fd == -1) {
			res = errno;
			MLGE("GPIO: Failed to open %s: %s", gpio_name, strerror(res));
			return res;
		}
		if (write(fd, gpio_active_low, len) == -1) {
			res = errno;
			MLGE("GPIO: Failed to write GPIO active low value: %s",
				strerror(res));
			return res;
		}
		if (close(fd)) {
			res = errno;
			MLGE("GPIO: Failed to close %s: %s", gpio_name,
				strerror(res));
			return res;
		}
		MLGD("GPIO: Write %s to GPIO %s", gpio_active_low, gpio_name);
	}
	return 0;
}
#endif /* BEFORE_KERNEL_2_6_33 */

static int mid_gpio_set_direction(struct mid_gpio_context* context, enum gpio_id gpio, int direction, int loc_ctrl_enable)
{
	int res = 0;
	int fd = -1;
	char gpio_name[GPIO_SYSFS_SZ];
	char *gpio_direction = NULL;
	res = snprintf(gpio_name, GPIO_SYSFS_SZ, GPIO_DIRECTION, context->gpio_base_path, context->mid_gpios_tab[gpio].gpio);
	if (res < 0) {
		res = errno;
		MLGE("GPIO: Failed to translate GPIO id to string: %s",
			strerror(res));
		return res;
	}
	switch (direction) {
	case GPIO_DIR_IN:
		gpio_direction = "in";
		break;
	case GPIO_DIR_OUT:
		gpio_direction = "out";
		break;
	case GPIO_DIR_OUT_LOW:
		gpio_direction = "low";
		break;
	case GPIO_DIR_OUT_HIGH:
		gpio_direction = "high";
		break;
	default:
		MLGE("GPIO: Invalid direction request for GPIO: %d", gpio);
		return -EMIDGPIO;
	}
	if (loc_ctrl_enable && mid_gpio_used(context, gpio)) {
		fd = open(gpio_name, O_WRONLY);
		if (fd == -1) {
			res = errno;
			MLGE("GPIO: Failed to open %s: %s", gpio_name, strerror(res));
			return res;
		}
		if (write(fd, gpio_direction, strlen(gpio_direction)) == -1) {
			res = errno;
			MLGE("GPIO: Failed to write GPIO direction value: %s",
				strerror(res));
			return res;
		}
		if (close(fd)) {
			res = errno;
			MLGE("GPIO: Failed to close %s: %s", gpio_name,
				strerror(res));
			return res;
		}
		MLGD("GPIO: Write %s to GPIO %s", gpio_direction, gpio_name);
	}
	return 0;
}

static int mid_gpio_init_direction(int direction, int init_val, int active_low)
{
	UNUSED(active_low);
	switch(direction) {
	case GPIO_DIR_IN:
		return GPIO_DIR_IN;
	case GPIO_DIR_OUT_LOW:
		return GPIO_DIR_OUT_LOW;
	case GPIO_DIR_OUT_HIGH:
		return GPIO_DIR_OUT_HIGH;
	case GPIO_DIR_OUT:
		if(init_val == 0)
			if(active_low == ACTIVE_LOW)
				return GPIO_DIR_OUT_HIGH;
			else
				return GPIO_DIR_OUT_LOW;
		else
			if(active_low == ACTIVE_LOW)
				return GPIO_DIR_OUT_LOW;
			else
				return GPIO_DIR_OUT_HIGH;
	default:
		MLGE("GPIO: Invalid GPIO direction: %d", direction);
		return -EMIDGPIO;
	}
}

int mid_gpio_assert(struct mid_gpio_context* context, enum gpio_id gpio)
{
	int res = 0;
	int assert = -1;

	if (mid_get_gpio_ctrl_noaclow(&context->mid_gpios_tab[gpio])) {
		if(mid_get_gpio_aclo(&context->mid_gpios_tab[gpio]) == ACTIVE_HIGH)
			assert = GPIO_ASSERT;
		else
			assert = GPIO_N_ASSERT;
	} else
		assert = GPIO_ASSERT;
	res = mid_gpio_set_value(context, gpio, assert, mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[gpio]));
	if(res)
		return res;
	else
		mid_set_gpio_val(&context->mid_gpios_tab[gpio], assert);
	return res;
}

int mid_gpio_deassert(struct mid_gpio_context* context, enum gpio_id gpio)
{
	int res = 0;
	int deassert = -1;
	if (mid_get_gpio_ctrl_noaclow(&context->mid_gpios_tab[gpio])) {
		if(mid_get_gpio_aclo(&context->mid_gpios_tab[gpio]) == ACTIVE_HIGH)
			deassert = GPIO_DEASSERT;
		else
			deassert = GPIO_N_DEASSERT;
	} else
		deassert = GPIO_DEASSERT;
	res = mid_gpio_set_value(context, gpio, deassert, mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[gpio]));
	if(res)
		return res;
	else
		mid_set_gpio_val(&context->mid_gpios_tab[gpio], deassert);
	return res;
}

void mid_gpio_release_all(struct mid_gpio_context* context)
{
	enum gpio_id id;
	if(context->unexport_fd == NULL) return;

	for(id = GPIO_ID_LOWEST; id <= GPIO_ID_HIGHEST; id++) {
		if(mid_gpio_used(context, id)
#ifdef BEFORE_KERNEL_2_6_33
		&& (id != GPIO_ID_RESOUT2)
		&& (id != GPIO_ID_PWRRSTIN)
#else
		&& (!context->gpio_no_xport)
#endif
		) {
			if (context->mid_gpios_tab[id].direction == GPIO_DIR_IN &&
					context->mid_gpios_tab[id].value != NONE)
				(void)mid_gpio_set_irq_wake(context, id, 0,
						mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[id]));
			if (mid_gpio_free(context, id))
				MLGE("GPIO: GPIO unexport error for GPIO id: %d", context->mid_gpios_tab[id].gpio);
		}
		else
			MLGW("GPIO: GPIO not used or not releasable from user space, id: %d, name: %s", id, gpio_display_name(id));
	}
}

static void dump_gpios(const struct gpio* gpios)
{
	MLGD("GPIO: GPIO table:");
	enum gpio_id i;
	for(i=GPIO_ID_LOWEST; i<=GPIO_ID_HIGHEST; i++)
	{
		const struct gpio* gpio=&gpios[i];
		MLGD("GPIO: %s: {gpio=%d, value=%d, direction=%d, aclow=%d, ctrl=%d, noaclow=%d}",
			gpio_display_name(i), gpio->gpio, gpio->value,
			gpio->direction, gpio->active_low,
			gpio->gpio_ctrl_enable, gpio->gpio_ctrl_noaclow
		);
	}
}

struct mid_gpio_context* mid_gpio_init(const struct gpio gpios[], const char* base_path, int ctrl_enable, int no_aclow, int no_xport, int no_dir)
{
	enum gpio_id id = 0;
	enum gpio_physical gpio = -1;
	int init_val = -1;
	int init_direction = -1;
	apr_status_t status;

	struct mid_gpio_context* context;
	dump_gpios(gpios);

	context=malloc(sizeof(struct mid_gpio_context));
	context->gpio_ctrl = ctrl_enable;
	context->gpio_no_aclow = no_aclow;
	context->gpio_no_xport = no_xport;
	context->gpio_no_dir = no_dir;
	context->pool = NULL;
	strncpy(context->gpio_base_path, base_path, sizeof(context->gpio_base_path));
	memcpy(context->mid_gpios_tab, gpios, GPIO_ID_COUNT * sizeof(struct gpio));
	context->unexport_fd = NULL;
	for(id = GPIO_ID_LOWEST; id <= GPIO_ID_HIGHEST; id++) {
		context->value_fds[id] = NULL;
		context->irqwake_fds[id] = NULL;
	}

	status = apr_pool_create(&context->pool, NULL);
	if (status != APR_SUCCESS) {
		char message[100];
		MLGE("GPIO: Failed to create memory pool for GPIO module: %s",
			apr_strerror(status, message, sizeof(message)));
		goto error;
	}

	MLGD("MID: Configuring GPIOs.");
	for(id = GPIO_ID_LOWEST; id <= GPIO_ID_HIGHEST; id++) {
		int res = 0;
		MLGD("GPIO: Configuring GPIO id: %d", id);
		gpio = context->mid_gpios_tab[id].gpio;
		context->mid_gpios_tab[id].gpio_ctrl_enable = ctrl_enable;
		context->mid_gpios_tab[id].gpio_ctrl_noaclow = no_aclow;
		if(
			mid_gpio_used(context, id)
#ifdef BEFORE_KERNEL_2_6_33
			&& (id != GPIO_ID_RESOUT2)
			&& (id != GPIO_ID_PWRRSTIN)
#endif
		) {
			char gpio_value_path[GPIO_SYSFS_SZ];
			char gpio_irqwake_path[GPIO_SYSFS_SZ];

			init_val = context->mid_gpios_tab[id].value;
			MLGD("GPIO: GPIO id: %d, name: %s, initialized as GPIO %d", id, gpio_display_name(id), gpio);
			if (!context->gpio_no_xport) {
				res =  mid_gpio_request(context, id);
				if(res){
					MLGE("GPIO: GPIO request error for GPIO: %d", gpio);
					goto error;
				}
			}
#ifndef BEFORE_KERNEL_2_6_33
			if(!context->mid_gpios_tab[id].gpio_ctrl_noaclow) {
				res = mid_gpio_set_active_low(context, id, context->mid_gpios_tab[id].active_low, mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[id]));
				if(res){
					MLGE("GPIO: GPIO init error for GPIO: %d (initial active low): %d", gpio, context->mid_gpios_tab[id].active_low);
					goto error;
				}
			}
#endif
			init_direction = mid_gpio_init_direction(context->mid_gpios_tab[id].direction,
				init_val, context->mid_gpios_tab[id].active_low);
			if (!context->gpio_no_dir) {
				res = mid_gpio_set_direction(context, id, init_direction, mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[id]));
				if(res){
					MLGE("GPIO: GPIO init error for GPIO: %d (initial direction): %d", gpio, init_direction);
					goto error;
				}
			}

			if (context->mid_gpios_tab[id].gpio != GPIO_PHYSICAL_INVALID) {
				snprintf(gpio_value_path, GPIO_SYSFS_SZ, GPIO_VALUE, context->gpio_base_path, context->mid_gpios_tab[id].gpio);
				status = apr_file_open(&context->value_fds[id], gpio_value_path, (init_direction == GPIO_DIR_IN)?APR_READ:APR_WRITE|APR_READ, 0, context->pool);
				if (status != APR_SUCCESS) {
					char message[100];
					MLGE("Failed to open GPIO file %s: %s", gpio_value_path, apr_strerror(status, message, sizeof(message)));
					goto error;
				}

				snprintf(gpio_irqwake_path, GPIO_SYSFS_SZ, GPIO_IRQ_WAKE, context->gpio_base_path,
				context->mid_gpios_tab[id].gpio);
				status = apr_file_open(&context->irqwake_fds[id], gpio_irqwake_path, APR_WRITE, 0, context->pool);
				if (status != APR_SUCCESS) {
					char message[100];
					MLGW("Failed to open GPIO file %s for writing: %s", gpio_irqwake_path, apr_strerror(status, message, sizeof(message)));
				}
			}

			if(init_direction != GPIO_DIR_IN) {
				if(init_val == ACTIVE)
					res = mid_gpio_assert(context, id);
				else
					res = mid_gpio_deassert(context, id);
				if(res){
					MLGE("GPIO: GPIO init error for GPIO: %d (initial value): %d", id, init_val);
					goto error;
				}
			}
#ifndef BEFORE_KERNEL_2_6_33
			else {
				res = mid_gpio_set_edge(context, id, init_val, mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[id]));
				if(res){
					MLGE("GPIO: GPIO init error for GPIO: %d (initial edge): %d", gpio, init_val);
					goto error;
				}

				if (init_val != NONE) {
					res = mid_gpio_set_irq_wake(context, id, 1,
							mid_get_gpio_ctrl_enable(&context->mid_gpios_tab[id]));
					if (res)
						MLGI("GPIO: IRQ wake is not supported by GPIO: %d", gpio);
				}
			}
#endif
		}
		else
			MLGW("GPIO: GPIO not used, id: %d, name: %s", id, gpio_display_name(id));
	}

	if (!context->gpio_no_xport) {
		char unexport_path[GPIO_SYSFS_SZ];
		snprintf(unexport_path, GPIO_SYSFS_SZ, GPIO_UNEXPORT, context->gpio_base_path);
		status = apr_file_open(&context->unexport_fd, unexport_path, APR_WRITE, 0, context->pool);
		if (status != APR_SUCCESS) {
			char message[100];
			MLGE("Failed to open GPIO unexport file: %s", apr_strerror(status, message, sizeof(message)));
			goto error;
		}
	}

	dump_gpios(context->mid_gpios_tab);

	return context;
error:
	if(context) {
		if(context->pool)
			apr_pool_destroy(context->pool);
		mid_gpio_release_all(context);
		free(context);
	}
	return NULL;
}

int mid_gpio_get_value(struct mid_gpio_context* context, enum gpio_id gpio_id)
{
	apr_file_t* fd = NULL;
	long val = 0;
	char gpio_value[GPIO_VALUE_SZ];
	apr_status_t status;
	apr_size_t num_read;
	apr_off_t start = 0;

	if (!context->gpio_ctrl || !mid_gpio_used(context, gpio_id)) {
		return 0;
	}
	fd = context->value_fds[gpio_id];
	if (fd == NULL) {
		MLGE("GPIO: File for requested GPIO %s was not open.", gpio_display_name(gpio_id));
		return -EMIDGPIO;
	}

	if (unlikely(apr_file_seek(fd, APR_SET, &start) != APR_SUCCESS)) {
		MLGE("GPIO: Failed to seek on fd for gpio %s.", gpio_display_name(gpio_id));
		return -EMIDGPIO;
	}

	status = apr_file_read_full(fd, gpio_value, GPIO_VALUE_SZ - 1, &num_read);
	if (unlikely(status != APR_SUCCESS)) {
		MLGE("GPIO: Failed to read GPIO value, error: %s",
			strerror(errno));
		return -EMIDGPIO;
	}
	gpio_value[num_read] = '\0';

	val = strtol(gpio_value, NULL, 10);
	/* TODO check over/under flow conditions */
	return (int)val;
}

apr_file_t* mid_gpio_get_fd(struct mid_gpio_context* context, enum gpio_id gpio)
{
	return context->value_fds[gpio];
}

bool mid_gpio_is_ctrl_enabled(struct mid_gpio_context* context)
{
        return context->gpio_ctrl;
}
